package cn.micro.tree;

import cn.micro.tree.bean.TTree;
import cn.micro.tree.bean.Tree;
import cn.micro.tree.bean.TreeNode;
import cn.micro.tree.bean.TreeNodeWrapper;
import cn.micro.tree.function.NodeConvertToT;
import cn.micro.tree.function.TConvertToNode;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 树构建帮助类
 */
public final class TreeHelper {

    private static final NodeConvertToT<Tree> NODE_CONVERT_TO_T = new NodeConvertToT<Tree>() {
        @Override
        public Tree convert(TreeNodeWrapper wrapper) {
            return (Tree) wrapper;
        }

        @Override
        public void children(Tree main, List<Tree> children) {
            main.setChildren(children);
        }
    };

    /**
     * 查找节点 nodeWrapper 所有父节点列表（祖链路径）
     *
     * @param nodeWrapper 当前节点
     * @param allWrappers 所有节点的集合
     * @return 当前节点所有父节点列表
     */
    public static List<TreeNodeWrapper> parent(TreeNodeWrapper nodeWrapper, List<TreeNodeWrapper> allWrappers) {
        return allWrappers.stream().filter(wrapper -> wrapper.getLft() < nodeWrapper.getLft() && wrapper.getRgt() > nodeWrapper.getRgt()).sorted(Comparator.comparing(TreeNodeWrapper::getLft)).collect(Collectors.toList());
    }

    /**
     * 节点集合转 Map ,key:节点id nid value: 节点实例对象
     *
     * @param wrappers 节点列表
     * @return 节点 Map 映射
     */
    public static Map<String, TreeNodeWrapper> toMap(List<TreeNodeWrapper> wrappers) {
        return wrappers.stream().collect(Collectors.toMap(TreeNode::getNid, wrapper -> wrapper));
    }

    /**
     * 查找节点 nodeWrapper 所有子节点列表
     *
     * @param nodeWrapper 当前节点
     * @param allWrappers 所有节点的集合
     * @return 当前节点所有子节点列表 (不包含当前节点)
     */
    public static List<TreeNodeWrapper> children(TreeNodeWrapper nodeWrapper, List<TreeNodeWrapper> allWrappers) {
        return children(nodeWrapper, allWrappers, false);
    }

    /**
     * 查找节点 nodeWrapper 子节点列表
     *
     * @param nodeWrapper 当前节点
     * @param allWrappers 所有节点的集合
     * @param isDirect    true:只返回直属节点 false:返回直属节点包括下下级节点
     * @return 当前节点子节点列表 (不包含当前节点)
     */
    public static List<TreeNodeWrapper> children(TreeNodeWrapper nodeWrapper, List<TreeNodeWrapper> allWrappers, Boolean isDirect) {
        return allWrappers.stream().filter(wrapper -> isDirect ? (wrapper.getLft() > nodeWrapper.getLft() && wrapper.getRgt() < nodeWrapper.getRgt() && wrapper.getLevel() == (nodeWrapper.getLevel() + 1)) : wrapper.getLft() > nodeWrapper.getLft() && wrapper.getRgt() < nodeWrapper.getRgt()).sorted(Comparator.comparing(TreeNodeWrapper::getLft)).collect(Collectors.toList());
    }

    /**
     * 树转 T 对象类型
     *
     * @param tree    树
     * @param convert 转换器
     * @param <T>     T 对象类型
     * @return T 类型树
     */
    public static <T> T convertToT(Tree tree, NodeConvertToT<T> convert) {
        T t = convert.convert(tree);
        List<Tree> trees = tree.getChildren();
        List<T> children = new ArrayList<>(trees.size());
        trees.forEach(tr -> {
            T trt = convertToT(tr, convert);
            children.add(trt);
        });
        convert.children(t, children);
        return t;
    }

    /**
     * 构建一棵树
     *
     * @param rootNode 根节点
     * @param nodes    节点数据
     * @param convert  入参类型和节点的转换
     * @param <T>      入参类型
     * @return 树
     */
    public static <T> Tree build(T rootNode, List<T> nodes, TConvertToNode<T> convert) {
        return build(convert.convert(rootNode), nodes.stream().map(convert::convert).collect(Collectors.toList()), NODE_CONVERT_TO_T);
    }

    /**
     * 构建一棵T类型树
     *
     * @param rootNode      根节点
     * @param nodes         节点数据
     * @param convertToNode 来源数据转节点转换器
     * @param convertToT    树节点转 T 类型转换器
     * @param <R>           来源类型
     * @param <T>           树T类型
     * @return T类型树
     */
    public static <R, T> T build(R rootNode, List<R> nodes, TConvertToNode<R> convertToNode, NodeConvertToT<T> convertToT) {
        return build(convertToNode.convert(rootNode), nodes.stream().map(convertToNode::convert).collect(Collectors.toList()), convertToT);
    }

    /**
     * 构建一棵树
     *
     * @param rootNode 根节点
     * @param nodes    节点数据
     * @return 树
     */
    public static Tree build(TreeNode rootNode, List<? extends TreeNode> nodes) {
        return build(rootNode, nodes, NODE_CONVERT_TO_T);
    }

    /**
     * 构建 T 类型一棵树
     *
     * @param rootNode 根节点
     * @param nodes    节点数据
     * @param convert  转换器
     * @param <T>      T 类型树结构
     * @return T 类型树
     */
    public static <T> T build(TreeNode rootNode, List<? extends TreeNode> nodes, NodeConvertToT<T> convert) {
        TTree<T> tree = new TTree<>(rootNode, convert);
        AtomicInteger integer = new AtomicInteger(1);
        tree.setPathNid(rootNode.getNid());
        tree.setPathName(rootNode.getName());
        tree.setLevel(1);
        tree.setLft(integer.get());
        Map<String, Tree> all = nodes.stream().map(treeNode -> new TTree<>(treeNode, convert)).collect(Collectors.toMap(TreeNode::getNid, treeNode -> treeNode));
        all.put(tree.getNid(), tree);
        all.forEach((k, v) -> Optional.ofNullable(all.get(v.getPid())).ifPresent(tr -> tr.getChildren().add(v)));
        children(tree, integer);
        tree.setRgt(integer.get());
        return tree.convertToT();
    }

    /**
     * 分解树为节点列表结构
     *
     * @param tree 树
     * @return 节点列表
     */
    public static List<TreeNodeWrapper> resolve(Tree tree) {
        return resolve(tree, wrapper -> wrapper);
    }

    /**
     * 分解树为节点列表结构
     *
     * @param tree    树
     * @param convert 转换器
     * @return 节点列表
     */
    public static <T> List<T> resolve(Tree tree, NodeConvertToT<T> convert) {
        List<T> treeNodeWrappers = new ArrayList<>();
        treeNodeWrappers.add(convert.convert(tree));
        resolveChildren(tree.getChildren(), treeNodeWrappers, convert);
        return treeNodeWrappers;
    }

    /**
     * 递归查询子节点
     *
     * @param rootNode 根节点
     * @return 根节点信息
     */
    private static void children(Tree rootNode, AtomicInteger integer) {
        List<Tree> trees = rootNode.getChildren();
        trees.sort(Comparator.comparing(TreeNode::getSort));
        Tree lastTree = trees.stream().reduce((first, second) -> second).orElse(null);
        trees.forEach((node) -> {
            node.setLft((integer.incrementAndGet()));
            node.setLevel((rootNode.getLevel() + 1));
            node.setPathName(rootNode.getPathName() + "," + node.getName());
            node.setPathNid(rootNode.getPathNid() + "," + node.getNid());
            children(node, integer);
            if ((node.getChildren() == null || node.getChildren().isEmpty())) {
                node.setRgt((integer.incrementAndGet()));
            }
            if (node.equals(lastTree)) {
                rootNode.setRgt((integer.incrementAndGet()));
            }
        });
    }

    /**
     * 递归收集子节点
     *
     * @param trees            节点列表
     * @param treeNodeWrappers 节点存储容器
     * @param convert          转换器
     */
    private static <T> void resolveChildren(List<Tree> trees, List<T> treeNodeWrappers, NodeConvertToT<T> convert) {
        trees.forEach(tree -> {
            treeNodeWrappers.add(convert.convert(tree));
            resolveChildren(tree.getChildren(), treeNodeWrappers, convert);
        });
    }

}
